package org.sourceprojects.lycia.incubator.test;

import java.lang.reflect.Method;

import org.junit.BeforeClass;
import org.junit.Test;
import org.sourceprojects.lycia.DefinitionBuilder;
import org.sourceprojects.lycia.ParserContext;
import org.sourceprojects.lycia.XmlParser;
import org.sourceprojects.lycia.exceptions.XmlParserParsingException;
import org.sourceprojects.lycia.fluent.FluentBuilder;
import org.w3c.dom.Element;

import static org.sourceprojects.lycia.XmlParserBuilder.GenerationStrategy.*;
import static org.sourceprojects.lycia.fluent.FluentBuilder.*;

public class BenchmarkReflectiveBytecodeTestCase {
	private static final String XML_DATA = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<xml><input id=\"5\">testvalue</input></xml>";

	private static XmlParser<StringBuilder> reflective;
	private static XmlParser<StringBuilder> bytecode;

	@BeforeClass
	@SuppressWarnings("unchecked")
	public static void setup() throws Exception {
		try {
			System.out.println("Preparing test-environment...");
			BenchmarkReflectiveBytecodeTestCase parser = new BenchmarkReflectiveBytecodeTestCase();

			Method method = BenchmarkReflectiveBytecodeTestCase.class
					.getMethod("parseInput", Element.class,
							ParserContext.class, DefinitionBuilder.class);

			// PREPARE GENERATION
			System.out.println("Using 10k pre-runs of generation to "
					+ "make HotSpot be called...");
			for (int i = 0; i < 10000; i++) {
				FluentBuilder<StringBuilder> builder = prepare();
				builder.configure(validateSchema(false));
				builder.configure(generationStrategy(ReflectiveGeneration));
				builder.parser(tag("input", method(method, parser)));
				XmlParser<StringBuilder> p = builder.build();
				p.getParserContext().setContextObject(new StringBuilder());
				p.parse(XML_DATA);
			}
			for (int i = 0; i < 10000; i++) {
				FluentBuilder<StringBuilder> builder = prepare();
				builder.configure(validateSchema(false));
				builder.configure(generationStrategy(BytecodeGeneration));
				builder.parser(tag("input", method(method, parser)));
				XmlParser<StringBuilder> p = builder.build();
				p.getParserContext().setContextObject(new StringBuilder());
				p.parse(XML_DATA);
			}

			System.out.println("Sleeping for 5secs to give "
					+ "HotSpot time to compile...");
			Thread.sleep(5000);

			// REFLECTIVE GENERATION
			System.out.println("Setting up Xml Parser for reflective "
					+ "parsing by fluent configuration...");

			long reflectiveTime = System.currentTimeMillis();

			FluentBuilder<StringBuilder> builder = prepare();
			builder.configure(validateSchema(false));
			builder.configure(generationStrategy(ReflectiveGeneration));
			builder.parser(tag("input", method(method, parser)));
			reflective = builder.build();
			reflective.getParserContext().setContextObject(new StringBuilder());

			reflectiveTime = System.currentTimeMillis() - reflectiveTime;

			System.out.println("Using 10k pre-runs to "
					+ "make HotSpot be called...");
			for (int i = 0; i < 10000; i++)
				reflective.parse(XML_DATA);

			System.out.println("Sleeping for 5secs to give "
					+ "HotSpot time to compile...");
			Thread.sleep(5000);

			// BYTECODE GENERATION
			System.out.println("Setting up Xml Parser for bytecode "
					+ "parsing by fluent configuration...");

			long bytecodeTime = System.currentTimeMillis();

			builder = prepare();
			builder.configure(validateSchema(false));
			builder.configure(generationStrategy(BytecodeGeneration));
			builder.parser(tag("input", method(method, parser)));
			bytecode = builder.build();
			bytecode.getParserContext().setContextObject(new StringBuilder());

			bytecodeTime = System.currentTimeMillis() - bytecodeTime;

			System.out.println("Using 10k pre-runs to "
					+ "make HotSpot be called...");
			for (int i = 0; i < 10000; i++)
				bytecode.parse(XML_DATA);

			System.out.println("Sleeping for 5secs to give "
					+ "HotSpot time to compile...");
			Thread.sleep(5000);

			System.out.println("Creationtime reflective: " + reflectiveTime
					+ "ms");
			System.out.println("Creationtime bytecode: " + bytecodeTime + "ms");
		} catch (Throwable e) {
			e.printStackTrace();
		}
	}

	@Test
	public void testReflectiveCalling() throws Exception {
		long[][] times = { { 10000000, -1 }, { 1000000, -1 }, { 100000, -1 },
				{ 10000, -1 }, { 1000, -1 } };

		System.out.println("Starting reflective testing...");

		for (long[] time : times) {
			System.out.println("Run parsing " + time[0] + " times...");
			long timeParsing = System.currentTimeMillis();

			long runs = time[0];
			for (int i = 0; i < runs; i++)
				reflective.parse(XML_DATA);

			time[1] = System.currentTimeMillis() - timeParsing;
		}

		for (long[] time : times)
			System.out.println("Runtime for " + time[0] + " runs: " + time[1]
					+ "ms");
	}

	@Test
	public void testBytecodeCalling() throws Exception {
		long[][] times = { { 10000000, -1 }, { 1000000, -1 }, { 100000, -1 },
				{ 10000, -1 }, { 1000, -1 } };

		System.out.println("Starting bytecode testing...");

		for (long[] time : times) {
			System.out.println("Run parsing " + time[0] + " times...");
			long timeParsing = System.currentTimeMillis();

			long runs = time[0];
			for (int i = 0; i < runs; i++)
				bytecode.parse(XML_DATA);

			time[1] = System.currentTimeMillis() - timeParsing;
		}

		for (long[] time : times)
			System.out.println("Runtime for " + time[0] + " runs: " + time[1]
					+ "ms");
	}

	public void parseInput(Element element,
			ParserContext<StringBuilder> parserContext,
			DefinitionBuilder definitionBuilder)
			throws XmlParserParsingException {

		final StringBuilder buffer = parserContext.getContextObject();
		buffer.append("id=").append(element.getAttribute("id")).append(";");
		buffer.append("value=").append(element.getTextContent());
	}
}